#pragma once
#include "libary/httplib.h"
#include "db.hpp"
#include "Log.hpp"
#include "mqttClient.hpp"
#include <unistd.h>
#include <functional>
using namespace std;

class Service
{
public:
    Service(user_table &ut) : _ut(ut)
    {
    }
    ~Service()
    {
    }

    void runModual()
    {
        function<void(const httplib::Request &, httplib::Response &)> start_ = std::bind(&Service::start, this, std::placeholders::_1, std::placeholders::_2);
        function<void(const httplib::Request &, httplib::Response &)> display_by_id_ = std::bind(&Service::display_by_id, this, std::placeholders::_1, std::placeholders::_2);
        function<void(const httplib::Request &, httplib::Response &)> display_by_name_ = std::bind(&Service::display_by_name, this, std::placeholders::_1, std::placeholders::_2);
        function<void(const httplib::Request &, httplib::Response &)> display_all_note_ = std::bind(&Service::display_all_note, this, std::placeholders::_1, std::placeholders::_2);
        function<void(const httplib::Request &, httplib::Response &)> mqtt_start_ = std::bind(&Service::mqtt_start, this, std::placeholders::_1, std::placeholders::_2);
        function<void(const httplib::Request &, httplib::Response &)> display_count_ = std::bind(&Service::display_count, this, std::placeholders::_1, std::placeholders::_2);
        function<void(const httplib::Request &, httplib::Response &)> display_lack_ = std::bind(&Service::display_lack, this, std::placeholders::_1, std::placeholders::_2);
        _svr.Get("/", start_);
        _svr.Post("/mqtt_start", mqtt_start_);
        _svr.Post("/display_by_id", display_by_id_);
        _svr.Post("/display_by_name", display_by_name_);
        _svr.Get("/display_all_note", display_all_note_);
        _svr.Get("/display_count", display_count_);
        _svr.Get("/display_lack", display_lack_);
        _svr.listen("0.0.0.0", 8080);
    }

public:
    void start(const httplib::Request &req, httplib::Response &resp)
    {
        string body;
        file_util::read("wwwroot/index.html", body);
        resp.body += body;
        resp.set_header("Content-Type", "text/html");
        resp.status = 200;
    }

    void display_by_id(const httplib::Request &req, httplib::Response &resp)
    {
        Json::Value val;
        json_util::deserialize(req.body, val);
        string id = val["id"].asString();

        Json::Value root;
        _ut.select_by_id(id, root);
        string body;
        json_util::serialize(root, body);
        
        resp.body.clear();
        resp.body += body;
        resp.set_header("Content-Type", "application/json");
        resp.status = 200;
    }

    void display_by_name(const httplib::Request &req, httplib::Response &resp)
    {
        Json::Value val;
        json_util::deserialize(req.body, val);
        string name = val["name"].asString();

        Json::Value root;
        _ut.select_by_name(name, root);
        string body;
        json_util::serialize(root, body);
        resp.body.clear();
        resp.body += body;
        resp.set_header("Content-Type", "application/json");
        resp.status = 200;
    }

    void display_all_note(const httplib::Request &req, httplib::Response &resp)
    {
        Json::Value root;
        _ut.select_all_note(root);
        string body;
        json_util::serialize(root, body);

        resp.body.clear();
        resp.body += body;
        resp.set_header("Content-Type", "application/json");
        resp.status = 200;
    }

    void display_count(const httplib::Request &req, httplib::Response &resp)
    {
        Json::Value root;
        _ut.count(root);
        string body;
        json_util::serialize(root, body);

        resp.body.clear();
        resp.body += body;
        resp.set_header("Content-Type", "application/json");
        resp.status = 200;
    }

    void display_lack(const httplib::Request &req, httplib::Response &resp)
    {
        Json::Value root;
        _ut.select_lack(root);
        string body;
        json_util::serialize(root, body);

        resp.body.clear();
        resp.body += body;
        resp.set_header("Content-Type", "application/json");
        resp.status = 200;
    }

    static void handler(int signo)
    {
        LOG(DEBUG, "time out");
        _isStartMqttClient = false;
    }

    void mqtt_start(const httplib::Request &req, httplib::Response &resp)
    {
        signal(SIGALRM, handler);
        Json::Value val;
        json_util::deserialize(req.body, val);
        string time = val["time"].asString();
        int duration = stoi(time);
        
        alarm(duration * 60);
        _isStartMqttClient = true;
        mqtt::async_client cli(SERVER_ADDRESS, CLIENT_ID);

        mqtt::connect_options connOpts;
        connOpts.set_clean_session(false);

        callback cb(cli, connOpts, _ut);
        cli.set_callback(cb);

        try
        {
            std::cout << "Connecting to the MQTT server..." << std::flush;
            cli.connect(connOpts, nullptr, cb);
        }
        catch (const mqtt::exception &exc)
        {
            std::cerr << "\nERROR: Unable to connect to MQTT server: '"
                      << SERVER_ADDRESS << "'" << exc << std::endl;
            return;
        }

        while (_isStartMqttClient)
        {
            sleep(1);
        }
        cout << "MQTT close" << endl;
    }

    

private:
    user_table &_ut;
    httplib::Server _svr;
    static bool _isStartMqttClient;
};

bool Service::_isStartMqttClient = false;
